﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using Microsoft.Win32;

using XmlVisualizer.Dialogs;
using XmlVisualizer.Models;
using XmlVisualizer.Controls;
using System.Configuration;

namespace XmlVisualizer.DiagramDesigner {

	/// <summary>
	/// Canvas on which the DesignerItems are placed.
	/// </summary>
	public partial class DesignerCanvas {
		#region Commands
		/// <summary>
		/// Expand command.
		/// </summary>
		public static RoutedCommand Expand = new RoutedCommand();
		/// <summary>
		/// Change stroke color command.
		/// </summary>
		public static RoutedCommand ChangeStrokeColor = new RoutedCommand();
		/// <summary>
		/// Change color command.
		/// </summary>
		public static RoutedCommand ChangeColor = new RoutedCommand();
		/// <summary>
		/// Change appearance command.
		/// </summary>
		public static RoutedCommand ChangeAppearance = new RoutedCommand();
		/// <summary>
		/// New text node command.
		/// </summary>
		public static RoutedCommand NewTextNode = new RoutedCommand();
		/// <summary>
		/// New element command.
		/// </summary>
		public static RoutedCommand NewElement = new RoutedCommand();
		/// <summary>
		/// Group command.
		/// </summary>
		public static RoutedCommand Group = new RoutedCommand();
		/// <summary>
		/// Ungroup command.
		/// </summary>
		public static RoutedCommand Ungroup = new RoutedCommand();
		/// <summary>
		/// Bring forward command.
		/// </summary>
		public static RoutedCommand BringForward = new RoutedCommand();
		/// <summary>
		/// Bring to front command.
		/// </summary>
		public static RoutedCommand BringToFront = new RoutedCommand();
		/// <summary>
		/// Send backward command.
		/// </summary>
		public static RoutedCommand SendBackward = new RoutedCommand();
		/// <summary>
		/// Send to back command.
		/// </summary>
		public static RoutedCommand SendToBack = new RoutedCommand();
		/// <summary>
		/// Align top command.
		/// </summary>
		public static RoutedCommand AlignTop = new RoutedCommand();
		/// <summary>
		/// Align vertically and center command.
		/// </summary>
		public static RoutedCommand AlignVerticalCenters = new RoutedCommand();
		/// <summary>
		/// Align bottom command.
		/// </summary>
		public static RoutedCommand AlignBottom = new RoutedCommand();
		/// <summary>
		/// Align left command.
		/// </summary>
		public static RoutedCommand AlignLeft = new RoutedCommand();
		/// <summary>
		/// Align horizontally and center command.
		/// </summary>
		public static RoutedCommand AlignHorizontalCenters = new RoutedCommand();
		/// <summary>
		/// Align right command.
		/// </summary>
		public static RoutedCommand AlignRight = new RoutedCommand();
		/// <summary>
		/// Distribute horizontally command.
		/// </summary>
		public static RoutedCommand DistributeHorizontal = new RoutedCommand();
		/// <summary>
		/// Distribute vertically command.
		/// </summary>
		public static RoutedCommand DistributeVertical = new RoutedCommand();
		/// <summary>
		/// Select all command.
		/// </summary>
		public static RoutedCommand SelectAll = new RoutedCommand();
		#endregion

		#region Constructors
		/// <summary>
		/// Initializes a new instance of the DesignerCanvas class. Adds command bindings for itself.
		/// </summary>
		public DesignerCanvas() {
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.Expand, Expand_Executed, Expand_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.ChangeStrokeColor, ChangeStrokeColor_Executed, ChangeStrokeColor_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.ChangeColor, ChangeColor_Executed, ChangeColor_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.ChangeAppearance, ChangeAppearance_Executed, ChangeAppearance_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.NewTextNode, NewTextNode_Executed, NewTextNode_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.NewElement, NewElement_Executed, NewElement_Enabled));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Print, Print_Executed));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Cut, Cut_Executed, Cut_Enabled));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, Copy_Executed, Copy_Enabled));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, Paste_Executed, Paste_Enabled));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Delete, Delete_Executed, Delete_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.Group, Group_Executed, Group_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.Ungroup, Ungroup_Executed, Ungroup_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.BringForward, BringForward_Executed, Order_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.BringToFront, BringToFront_Executed, Order_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.SendBackward, SendBackward_Executed, Order_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.SendToBack, SendToBack_Executed, Order_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignTop, AlignTop_Executed, Align_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignVerticalCenters, AlignVerticalCenters_Executed, Align_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignBottom, AlignBottom_Executed, Align_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignLeft, AlignLeft_Executed, Align_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignHorizontalCenters, AlignHorizontalCenters_Executed, Align_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.AlignRight, AlignRight_Executed, Align_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.DistributeHorizontal, DistributeHorizontal_Executed, Distribute_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.DistributeVertical, DistributeVertical_Executed, Distribute_Enabled));
			this.CommandBindings.Add(new CommandBinding(DesignerCanvas.SelectAll, SelectAll_Executed));
			SelectAll.InputGestures.Add(new KeyGesture(Key.A, ModifierKeys.Control)); // ctrl+A = select all

			this.AllowDrop = true;
			Clipboard.Clear();
		}
		#endregion
			
		#region Command handlers

		#region Expand Command
		private void Expand_Executed(object sender, ExecutedRoutedEventArgs e) {
			DesignerItem designerItem = selectionService.CurrentSelection.OfType<DesignerItem>().FirstOrDefault();
			if (designerItem != null) {
				XNodeInfo info = designerItem.Info;
				ElementAddress address = info.ElementInfo;

				Dialogs.DialogRange dialog = new DialogRange();
				string range = dialog.ShowModal();

				Document.CurrentController.Expand(info.Node as XElement, address, range, 1);
			}
		}

		private void Expand_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			if (Document.Current.Model.Mode == Mode.BigXml) {
				int count = selectionService.CurrentSelection.OfType<DesignerItem>().Count();
				if (count > 1)
					e.CanExecute = false;
				else
					e.CanExecute = true;
			} else {
				e.CanExecute = false;
			}
		}
		#endregion

		#region ChangeStrokeColor Command
		private void ChangeStrokeColor_Executed(object sender, ExecutedRoutedEventArgs e) {
			Color fillColor = Colors.Black;
			int count = selectionService.CurrentSelection.OfType<DesignerItem>().Count();
			if (count > 0) {
				ColorPickerDialog cPicker = new ColorPickerDialog();
				cPicker.StartingColor = Properties.Settings.Default.defaultStrokeColor;

				bool? dialogResult = cPicker.ShowDialog();
				if (dialogResult != null && (bool)dialogResult == true) {
					fillColor = cPicker.SelectedColor;
				}

				foreach (DesignerItem item in selectionService.CurrentSelection.OfType<DesignerItem>()) {
					item.StrokeBrush = new SolidColorBrush(fillColor);
				}
				selectionService.ClearSelection();
			}

			Document.Current.Modified = true;
		}

		private void ChangeStrokeColor_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			e.CanExecute = true;
		}
		#endregion

		#region ChangeColor Command
		private void ChangeColor_Executed(object sender, ExecutedRoutedEventArgs e) {
			Color fillColor = Colors.Black;
			int count = selectionService.CurrentSelection.OfType<DesignerItem>().Count();
			if (count > 0) {
				ColorPickerDialog cPicker = new ColorPickerDialog();
				cPicker.StartingColor = Properties.Settings.Default.defaultFillColor;

				bool? dialogResult = cPicker.ShowDialog();
				if (dialogResult != null && (bool)dialogResult == true) {
					fillColor = cPicker.SelectedColor;
				}

				foreach (DesignerItem item in selectionService.CurrentSelection.OfType<DesignerItem>()) {
					item.FillBrush = new SolidColorBrush(fillColor);
					item.OldFillBrush = item.FillBrush;
				}
				selectionService.ClearSelection();
			}

			Document.Current.Modified = true;
		}

		private void ChangeColor_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			e.CanExecute = true;
		}
		#endregion

		#region ChangeAppearance Command
		private void ChangeAppearance_Executed(object sender, ExecutedRoutedEventArgs e) {
			int count = selectionService.CurrentSelection.OfType<DesignerItem>().Count();

			if (count > 0) {
				DialogChangeAppearance dialog = new DialogChangeAppearance();
				Point pos = Mouse.GetPosition(this);
				Point pos2 = PointToScreen(pos);
				dialog.Left = pos2.X;
				dialog.Top = pos2.Y;
				string appearance = dialog.ShowModal();
				if (!String.IsNullOrEmpty(appearance)) {
					ControlTemplate template = (ControlTemplate)FindResource(appearance);
					if (template != null) {
						foreach (DesignerItem item in selectionService.CurrentSelection.OfType<DesignerItem>()) {
							item.AppearanceTemplate = template;
							item.Info.AppearanceTemplate = appearance;
						}
					}
				}
			}

			Document.Current.Modified = true;
		}

		private void ChangeAppearance_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			e.CanExecute = true;
		}
		#endregion

		#region NewTextNode Command
		private void NewTextNode_Executed(object sender, ExecutedRoutedEventArgs e) {
			DialogAddNewTextNode dialog = new DialogAddNewTextNode();
			Point pos = Mouse.GetPosition(this);
			Point pos2 = PointToScreen(pos);
			dialog.Left = pos2.X;
			dialog.Top = pos2.Y;
			XNodeInfo info = dialog.ShowModal();
			if (info != null) {
				Document.CurrentController.AddNewElement(e.Parameter as XElement, info);
			}
		}

		private void NewTextNode_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			if (e.Parameter is XElement)
				e.CanExecute = true;
			else if (e.Parameter is XText) {
				e.CanExecute = false;
			} else {
				e.CanExecute = true;
			}
		}
		#endregion

		#region NewElement Command
		private void NewElement_Executed(object sender, ExecutedRoutedEventArgs e) {
			DialogAddNewElement dialog = new DialogAddNewElement();
			Point pos = Mouse.GetPosition(this);
			Point pos2 = PointToScreen(pos);
			dialog.Left = pos2.X;
			dialog.Top = pos2.Y;

			XNodeInfo newElement = dialog.ShowModal();
			if (newElement != null) {
				if (newElement.Position.X == 0 && newElement.Position.Y == 0)
					newElement.Position = new Point(pos.X, pos.Y);

				Document.CurrentController.AddNewElement(e.Parameter as XElement, newElement);
			}
		}

		private void NewElement_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			if (e.Parameter is XElement)
				e.CanExecute = true;
			else if (e.Parameter is XText) {
				e.CanExecute = false;
			} else {
				e.CanExecute = true;
			}
		}
		#endregion

		#region Print Command

		private void Print_Executed(object sender, ExecutedRoutedEventArgs e) {
			selectionService.ClearSelection();

			PrintDialog printDialog = new PrintDialog();

			if (true == printDialog.ShowDialog()) {
				printDialog.PrintVisual(this, "WPF Diagram");
			}
		}

		#endregion

		#region Copy Command

		private void Copy_Executed(object sender, ExecutedRoutedEventArgs e) {
			CopyCurrentSelection();
		}

		private void Copy_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			e.CanExecute = selectionService.CurrentSelection.Count() > 0;
		}

		#endregion

		#region Paste Command

		private void Paste_Executed(object sender, ExecutedRoutedEventArgs e) {
			XElement root = LoadSerializedDataFromClipBoard();

			if (root == null)
				return;

			// create DesignerItems
			Dictionary<Guid, Guid> mappingOldToNewIDs = new Dictionary<Guid, Guid>();
			List<ISelectable> newItems = new List<ISelectable>();
			IEnumerable<XElement> itemsXML = root.Elements("DesignerItems").Elements("DesignerItem");

			double offsetX = Double.Parse(root.Attribute("OffsetX").Value, CultureInfo.InvariantCulture);
			double offsetY = Double.Parse(root.Attribute("OffsetY").Value, CultureInfo.InvariantCulture);

			foreach (XElement itemXML in itemsXML) {
				Guid oldID = new Guid(itemXML.Element("ID").Value);
				Guid newID = Guid.NewGuid();
				mappingOldToNewIDs.Add(oldID, newID);
				DesignerItem item = DeserializeDesignerItem(itemXML, newID, offsetX, offsetY, null);
				this.Children.Add(item);
				SetConnectorDecoratorTemplate(item);
				newItems.Add(item);
			}

			// update group hierarchy
			selectionService.ClearSelection();
			foreach (DesignerItem el in newItems) {
				if (el.ParentID != Guid.Empty)
					el.ParentID = mappingOldToNewIDs[el.ParentID];
			}


			foreach (DesignerItem item in newItems) {
				if (item.ParentID == Guid.Empty) {
					selectionService.AddToSelection(item);
				}
			}

			// create Connections
			IEnumerable<XElement> connectionsXML = root.Elements("Connections").Elements("Connection");
			foreach (XElement connectionXML in connectionsXML) {
				Guid oldSourceID = new Guid(connectionXML.Element("SourceID").Value);
				Guid oldSinkID = new Guid(connectionXML.Element("SinkID").Value);

				if (mappingOldToNewIDs.ContainsKey(oldSourceID) && mappingOldToNewIDs.ContainsKey(oldSinkID)) {
					Guid newSourceID = mappingOldToNewIDs[oldSourceID];
					Guid newSinkID = mappingOldToNewIDs[oldSinkID];

					String sourceConnectorName = connectionXML.Element("SourceConnectorName").Value;
					String sinkConnectorName = connectionXML.Element("SinkConnectorName").Value;

					Connector sourceConnector = GetConnector(newSourceID, sourceConnectorName);
					Connector sinkConnector = GetConnector(newSinkID, sinkConnectorName);

					Connection connection = new Connection(sourceConnector, sinkConnector);
					Canvas.SetZIndex(connection, Int32.Parse(connectionXML.Element("zIndex").Value));
					this.Children.Add(connection);

					selectionService.AddToSelection(connection);
				}
			}

			DesignerCanvas.BringToFront.Execute(null, this);

			// update paste offset
			root.Attribute("OffsetX").Value = (offsetX + 10).ToString();
			root.Attribute("OffsetY").Value = (offsetY + 10).ToString();
			Clipboard.Clear();
			Clipboard.SetData(DataFormats.Xaml, root);
		}

		private void Paste_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			e.CanExecute = Clipboard.ContainsData(DataFormats.Xaml);
		}

		#endregion

		#region Delete Command

		private void Delete_Executed(object sender, ExecutedRoutedEventArgs e) {
			bool? recursively = DeleteRecursivelyDialog();
			DeleteCurrentSelection(recursively);
		}

		private void Delete_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			e.CanExecute = this.selectionService.CurrentSelection.Count() > 0;
		}

		#endregion

		#region Cut Command

		private void Cut_Executed(object sender, ExecutedRoutedEventArgs e) {
			CopyCurrentSelection();

			bool? recursively = DeleteRecursivelyDialog();
			DeleteCurrentSelection(recursively);
		}

		private void Cut_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			e.CanExecute = this.selectionService.CurrentSelection.Count() > 0;
		}

		#endregion

		#region Group Command

		private void Group_Executed(object sender, ExecutedRoutedEventArgs e) {
			var items = from item in this.selectionService.CurrentSelection.OfType<DesignerItem>()
						where item.ParentID == Guid.Empty
						select item;

			Rect rect = GetBoundingRectangle(items);

			DesignerItem groupItem = new DesignerItem();
			groupItem.IsGroup = true;
			groupItem.Width = rect.Width;
			groupItem.Height = rect.Height;
			Canvas.SetLeft(groupItem, rect.Left);
			Canvas.SetTop(groupItem, rect.Top);
			Canvas groupCanvas = new Canvas();
			groupItem.Content = groupCanvas;
			Canvas.SetZIndex(groupItem, this.Children.Count);
			this.Children.Add(groupItem);

			foreach (DesignerItem item in items)
				item.ParentID = groupItem.ID;

			this.selectionService.SelectItem(groupItem);
		}

		private void Group_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			int count = (from item in selectionService.CurrentSelection.OfType<DesignerItem>()
						 where item.ParentID == Guid.Empty
						 select item).Count();

			e.CanExecute = count > 1;
		}

		#endregion

		#region Ungroup Command

		private void Ungroup_Executed(object sender, ExecutedRoutedEventArgs e) {
			var groups = (from item in selectionService.CurrentSelection.OfType<DesignerItem>()
						  where item.IsGroup && item.ParentID == Guid.Empty
						  select item).ToArray();

			foreach (DesignerItem groupRoot in groups) {
				var children = from child in selectionService.CurrentSelection.OfType<DesignerItem>()
							   where child.ParentID == groupRoot.ID
							   select child;

				foreach (DesignerItem child in children)
					child.ParentID = Guid.Empty;

				this.selectionService.RemoveFromSelection(groupRoot);
				this.Children.Remove(groupRoot);
				UpdateZIndex();
			}
		}

		private void Ungroup_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			var groupedItem = from item in selectionService.CurrentSelection.OfType<DesignerItem>()
							  where item.ParentID != Guid.Empty
							  select item;


			e.CanExecute = groupedItem.Count() > 0;
		}

		#endregion

		#region BringForward Command

		private void BringForward_Executed(object sender, ExecutedRoutedEventArgs e) {
			List<UIElement> ordered = (from item in selectionService.CurrentSelection
									   orderby Canvas.GetZIndex(item as UIElement) descending
									   select item as UIElement).ToList();

			int count = this.Children.Count;

			for (int i = 0; i < ordered.Count; i++) {
				int currentIndex = Canvas.GetZIndex(ordered[i]);
				int newIndex = Math.Min(count - 1 - i, currentIndex + 1);
				if (currentIndex != newIndex) {
					Canvas.SetZIndex(ordered[i], newIndex);
					IEnumerable<UIElement> it = this.Children.OfType<UIElement>().Where(item => Canvas.GetZIndex(item) == newIndex);

					foreach (UIElement elm in it) {
						if (elm != ordered[i]) {
							Canvas.SetZIndex(elm, currentIndex);
							break;
						}
					}
				}
			}
		}

		private void Order_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			//e.CanExecute = SelectionService.CurrentSelection.Count() > 0;
			e.CanExecute = true;
		}

		#endregion

		#region BringToFront Command

		private void BringToFront_Executed(object sender, ExecutedRoutedEventArgs e) {
			List<UIElement> selectionSorted = (from item in selectionService.CurrentSelection
											   orderby Canvas.GetZIndex(item as UIElement) ascending
											   select item as UIElement).ToList();

			List<UIElement> childrenSorted = (from UIElement item in this.Children
											  orderby Canvas.GetZIndex(item as UIElement) ascending
											  select item as UIElement).ToList();

			int i = 0;
			int j = 0;
			foreach (UIElement item in childrenSorted) {
				if (selectionSorted.Contains(item)) {
					int idx = Canvas.GetZIndex(item);
					Canvas.SetZIndex(item, childrenSorted.Count - selectionSorted.Count + j++);
				} else {
					Canvas.SetZIndex(item, i++);
				}
			}
		}

		#endregion

		#region SendBackward Command

		private void SendBackward_Executed(object sender, ExecutedRoutedEventArgs e) {
			List<UIElement> ordered = (from item in selectionService.CurrentSelection
									   orderby Canvas.GetZIndex(item as UIElement) ascending
									   select item as UIElement).ToList();

			int count = this.Children.Count;

			for (int i = 0; i < ordered.Count; i++) {
				int currentIndex = Canvas.GetZIndex(ordered[i]);
				int newIndex = Math.Max(i, currentIndex - 1);
				if (currentIndex != newIndex) {
					Canvas.SetZIndex(ordered[i], newIndex);
					IEnumerable<UIElement> it = this.Children.OfType<UIElement>().Where(item => Canvas.GetZIndex(item) == newIndex);

					foreach (UIElement elm in it) {
						if (elm != ordered[i]) {
							Canvas.SetZIndex(elm, currentIndex);
							break;
						}
					}
				}
			}
		}

		#endregion

		#region SendToBack Command

		private void SendToBack_Executed(object sender, ExecutedRoutedEventArgs e) {
			List<UIElement> selectionSorted = (from item in selectionService.CurrentSelection
											   orderby Canvas.GetZIndex(item as UIElement) ascending
											   select item as UIElement).ToList();

			List<UIElement> childrenSorted = (from UIElement item in this.Children
											  orderby Canvas.GetZIndex(item as UIElement) ascending
											  select item as UIElement).ToList();
			int i = 0;
			int j = 0;
			foreach (UIElement item in childrenSorted) {
				if (selectionSorted.Contains(item)) {
					int idx = Canvas.GetZIndex(item);
					Canvas.SetZIndex(item, j++);

				} else {
					Canvas.SetZIndex(item, selectionSorted.Count + i++);
				}
			}
		}

		#endregion

		#region AlignTop Command

		private void AlignTop_Executed(object sender, ExecutedRoutedEventArgs e) {
			var selectedItems = from item in selectionService.CurrentSelection.OfType<DesignerItem>()
								where item.ParentID == Guid.Empty
								select item;

			if (selectedItems.Count() > 1) {
				double top = Canvas.GetTop(selectedItems.First());

				foreach (DesignerItem item in selectedItems) {
					double delta = top - Canvas.GetTop(item);
					foreach (DesignerItem di in selectionService.GetGroupMembers(item)) {
						Canvas.SetTop(di, Canvas.GetTop(di) + delta);
					}
				}
			}
		}

		private void Align_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			//var groupedItem = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
			//                  where item.ParentID == Guid.Empty
			//                  select item;


			//e.CanExecute = groupedItem.Count() > 1;
			e.CanExecute = true;
		}

		#endregion

		#region AlignVerticalCenters Command

		private void AlignVerticalCenters_Executed(object sender, ExecutedRoutedEventArgs e) {
			var selectedItems = from item in selectionService.CurrentSelection.OfType<DesignerItem>()
								where item.ParentID == Guid.Empty
								select item;

			if (selectedItems.Count() > 1) {
				double bottom = Canvas.GetTop(selectedItems.First()) + selectedItems.First().Height / 2;

				foreach (DesignerItem item in selectedItems) {
					double delta = bottom - (Canvas.GetTop(item) + item.Height / 2);
					foreach (DesignerItem di in selectionService.GetGroupMembers(item)) {
						Canvas.SetTop(di, Canvas.GetTop(di) + delta);
					}
				}
			}
		}

		#endregion

		#region AlignBottom Command

		private void AlignBottom_Executed(object sender, ExecutedRoutedEventArgs e) {
			var selectedItems = from item in selectionService.CurrentSelection.OfType<DesignerItem>()
								where item.ParentID == Guid.Empty
								select item;

			if (selectedItems.Count() > 1) {
				double bottom = Canvas.GetTop(selectedItems.First()) + selectedItems.First().Height;

				foreach (DesignerItem item in selectedItems) {
					double delta = bottom - (Canvas.GetTop(item) + item.Height);
					foreach (DesignerItem di in selectionService.GetGroupMembers(item)) {
						Canvas.SetTop(di, Canvas.GetTop(di) + delta);
					}
				}
			}
		}

		#endregion

		#region AlignLeft Command

		private void AlignLeft_Executed(object sender, ExecutedRoutedEventArgs e) {
			var selectedItems = from item in selectionService.CurrentSelection.OfType<DesignerItem>()
								where item.ParentID == Guid.Empty
								select item;

			if (selectedItems.Count() > 1) {
				double left = Canvas.GetLeft(selectedItems.First());

				foreach (DesignerItem item in selectedItems) {
					double delta = left - Canvas.GetLeft(item);
					foreach (DesignerItem di in selectionService.GetGroupMembers(item)) {
						Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
					}
				}
			}
		}

		#endregion

		#region AlignHorizontalCenters Command

		private void AlignHorizontalCenters_Executed(object sender, ExecutedRoutedEventArgs e) {
			var selectedItems = from item in selectionService.CurrentSelection.OfType<DesignerItem>()
								where item.ParentID == Guid.Empty
								select item;

			if (selectedItems.Count() > 1) {
				double center = Canvas.GetLeft(selectedItems.First()) + selectedItems.First().Width / 2;

				foreach (DesignerItem item in selectedItems) {
					double delta = center - (Canvas.GetLeft(item) + item.Width / 2);
					foreach (DesignerItem di in selectionService.GetGroupMembers(item)) {
						Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
					}
				}
			}
		}

		#endregion

		#region AlignRight Command

		private void AlignRight_Executed(object sender, ExecutedRoutedEventArgs e) {
			var selectedItems = from item in selectionService.CurrentSelection.OfType<DesignerItem>()
								where item.ParentID == Guid.Empty
								select item;

			if (selectedItems.Count() > 1) {
				double right = Canvas.GetLeft(selectedItems.First()) + selectedItems.First().Width;

				foreach (DesignerItem item in selectedItems) {
					double delta = right - (Canvas.GetLeft(item) + item.Width);
					foreach (DesignerItem di in selectionService.GetGroupMembers(item)) {
						Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
					}
				}
			}
		}

		#endregion

		#region DistributeHorizontal Command

		private void DistributeHorizontal_Executed(object sender, ExecutedRoutedEventArgs e) {
			var selectedItems = from item in selectionService.CurrentSelection.OfType<DesignerItem>()
								where item.ParentID == Guid.Empty
								let itemLeft = Canvas.GetLeft(item)
								orderby itemLeft
								select item;

			if (selectedItems.Count() > 1) {
				double left = Double.MaxValue;
				double right = Double.MinValue;
				double sumWidth = 0;
				foreach (DesignerItem item in selectedItems) {
					left = Math.Min(left, Canvas.GetLeft(item));
					right = Math.Max(right, Canvas.GetLeft(item) + item.Width);
					sumWidth += item.Width;
				}

				double distance = Math.Max(0, (right - left - sumWidth) / (selectedItems.Count() - 1));
				double offset = Canvas.GetLeft(selectedItems.First());

				foreach (DesignerItem item in selectedItems) {
					double delta = offset - Canvas.GetLeft(item);
					foreach (DesignerItem di in selectionService.GetGroupMembers(item)) {
						Canvas.SetLeft(di, Canvas.GetLeft(di) + delta);
					}
					offset = offset + item.Width + distance;
				}
			}
		}

		private void Distribute_Enabled(object sender, CanExecuteRoutedEventArgs e) {
			//var groupedItem = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
			//                  where item.ParentID == Guid.Empty
			//                  select item;


			//e.CanExecute = groupedItem.Count() > 1;
			e.CanExecute = true;
		}

		#endregion

		#region DistributeVertical Command

		private void DistributeVertical_Executed(object sender, ExecutedRoutedEventArgs e) {
			var selectedItems = from item in selectionService.CurrentSelection.OfType<DesignerItem>()
								where item.ParentID == Guid.Empty
								let itemTop = Canvas.GetTop(item)
								orderby itemTop
								select item;

			if (selectedItems.Count() > 1) {
				double top = Double.MaxValue;
				double bottom = Double.MinValue;
				double sumHeight = 0;
				foreach (DesignerItem item in selectedItems) {
					top = Math.Min(top, Canvas.GetTop(item));
					bottom = Math.Max(bottom, Canvas.GetTop(item) + item.Height);
					sumHeight += item.Height;
				}

				double distance = Math.Max(0, (bottom - top - sumHeight) / (selectedItems.Count() - 1));
				double offset = Canvas.GetTop(selectedItems.First());

				foreach (DesignerItem item in selectedItems) {
					double delta = offset - Canvas.GetTop(item);
					foreach (DesignerItem di in selectionService.GetGroupMembers(item)) {
						Canvas.SetTop(di, Canvas.GetTop(di) + delta);
					}
					offset = offset + item.Height + distance;
				}
			}
		}

		#endregion

		#region SelectAll Command

		private void SelectAll_Executed(object sender, ExecutedRoutedEventArgs e) {
			selectionService.SelectAll();
		}

		#endregion
		#endregion

		#region Helper Methods
		private Document GetParentDocument() {
			DependencyObject obj = VisualTreeHelper.GetParent(this);
			if (obj == null)
				return null;
			while (!(obj is Document)) {
				obj = VisualTreeHelper.GetParent(obj);
				if (obj == null)
					return null;
			}
			return (Document)obj;
		}

		private bool? DeleteRecursivelyDialog() {
			DialogDeleteRecursively dialog = new DialogDeleteRecursively();

			Point pos = Mouse.GetPosition(this);
			Point pos2 = PointToScreen(pos);
			dialog.Left = pos2.X;
			dialog.Top = pos2.Y;

			MessageBoxResult res = dialog.ShowModal();

			bool? recursively = false;
			if (res == MessageBoxResult.Yes)
				recursively = true;
			else if (res == MessageBoxResult.Cancel)
				recursively = null;

			return recursively;
		}

		private void Reset() {
			this.Children.Clear();
			this.selectionService.ClearSelection();
		}

		/// <summary>
		/// Loads custom Xmv format from file.
		/// </summary>
		/// <param name="filename">file to load</param>
		public void OpenXmv(string filename) {
			try {
				
				XElement root = null;
				try {
					root = XElement.Load(filename);
				} catch (XmlException ex) {
					throw new FileFormatException("File is corrupted.", ex);
				}

				if (root == null)
					return;

				// Reset
				Reset();
				Document.Current.Reset();
				Document.CurrentController.ResetModel();

				// Find XmlModel part
				XElement xmlModel = root.Element("XmlModel");
				if (xmlModel == null)
					throw new FileFormatException();
				else
					// Deserialize it
					Document.Current.Model.Deserialize(xmlModel);

				// Get deserialized info from model
				XDocument document = Document.Current.Model.GetDocument();
				IEnumerable<XElement> documentParts = Document.Current.Model.GetDocumentParts();

				// Deserialize DesignerItems
				IEnumerable<XElement> itemsXML = root.Elements("DesignerItems").Elements("DesignerItem");
				foreach (XElement itemXML in itemsXML) {
					Guid id = new Guid(itemXML.Element("ID").Value);
					DesignerItem item = DeserializeDesignerItem(itemXML, id, 0, 0, documentParts);
					this.Children.Add(item);
					SetConnectorDecoratorTemplate(item);
				}

				this.InvalidateVisual();

				DeserializeConnections(root);

				Document.CurrentController.SerializationDone();
				
				Document.Current.FileName = filename;
			} catch (FileFormatException ex) {
				Reset();
				Document.Current.Reset();
				Document.CurrentController.ResetModel();
				Exceptions.ExceptionHandling.HandleException(ex);
			} catch (XmlException ex) {
				Exceptions.ExceptionHandling.HandleException(ex);
			}
		}

		public void SaveXmv(string filename) {
			Document.CurrentController.PrepareForSerialization();
			XElement xmlModel = Document.Current.Model.Serialize();

			IEnumerable<DesignerItem> designerItems = this.Children.OfType<DesignerItem>();
			IEnumerable<Connection> connections = this.Children.OfType<Connection>();

			XElement designerItemsXML = SerializeDesignerItems(designerItems);
			XElement connectionsXML = SerializeConnections(connections);
			XElement document = new XElement("Document");
			document.Add(Document.Current.Model.GetXmlText());

			XElement root = new XElement("Root");
			root.Add(xmlModel);
			root.Add(designerItemsXML);
			root.Add(connectionsXML);

			root.Save(filename);
			Document.CurrentController.SerializationDone();
			
			Document.Current.FileName = filename;
		}

		private XElement LoadSerializedDataFromClipBoard() {
			if (Clipboard.ContainsData(DataFormats.Xaml)) {
				String clipboardData = Clipboard.GetData(DataFormats.Xaml) as String;

				if (String.IsNullOrEmpty(clipboardData))
					return null;
				try {
					return XElement.Load(new StringReader(clipboardData));
				} catch (Exception e) {
					MessageBox.Show(e.StackTrace, e.Message, MessageBoxButton.OK, MessageBoxImage.Error);
				}
			}

			return null;
		}

		private XElement SerializeDesignerItems(IEnumerable<DesignerItem> designerItems) {
			try {
				XNamespace ns = ConfigurationManager.AppSettings["xmlvisualizerNamespace"];

				XElement serializedItems = new XElement("DesignerItems",
										   from item in designerItems
										   let contentXaml = XamlWriter.Save(((DesignerItem)item).Content)
										   //let info = ParentDocument.DisplayedNodes[item.Node] as XNodeInfo
										   let info = item.Info as XNodeInfo
										   select new XElement("DesignerItem",
													  new XElement("Left", Canvas.GetLeft(item)),
													  new XElement("Top", Canvas.GetTop(item)),
													  new XElement("Width", item.ActualWidth),
													  new XElement("Height", item.ActualHeight),
													  new XElement("ID", item.ID),
													  new XElement("zIndex", Canvas.GetZIndex(item)),
													  new XElement("IsGroup", item.IsGroup),
													  new XElement("ParentID", item.ParentID),
													  new XElement("ContentType", item.Content.GetType().ToString()),
													  new XElement("Padding", item.Padding),
													  new XElement("FillBrush", item.FillBrush),
													  new XElement("StrokeBrush", item.StrokeBrush),
													  info.Serialize()
												  )
									   );
				return serializedItems;
			} catch (ConfigurationErrorsException ex) {
				Exceptions.ExceptionHandling.HandleException(ex);
			} catch (XmlException ex) {
				Exceptions.ExceptionHandling.HandleException(ex);
			} catch (NullReferenceException ex) {
				Exceptions.ExceptionHandling.HandleException(ex);
			}
			return null;
		}

		private XElement SerializeConnections(IEnumerable<Connection> connections) {
			var serializedConnections = new XElement("Connections",
						   from connection in connections
						   select new XElement("Connection",
									  new XElement("SourceID", connection.Source.ParentDesignerItem.ID),
									  new XElement("SinkID", connection.Sink.ParentDesignerItem.ID),
									  new XElement("SourceConnectorName", connection.Source.Name),
									  new XElement("SinkConnectorName", connection.Sink.Name),
									  new XElement("SourceArrowSymbol", connection.SourceArrowSymbol),
									  new XElement("SinkArrowSymbol", connection.SinkArrowSymbol),
									  new XElement("zIndex", Canvas.GetZIndex(connection))
									 )
								  );

			return serializedConnections;
		}

		/// <summary>
		/// Deserializes connections.
		/// </summary>
		/// <param name="root">Xml piece with serialized connections.</param>
		/// <exception cref="System.IO.FileFormaException">file is corrupted</exception>
		private void DeserializeConnections(XElement root) {
			try {
				IEnumerable<XElement> connectionsXML = root.Elements("Connections").Elements("Connection");
				foreach (XElement connectionXML in connectionsXML) {
					Guid sourceID = new Guid(connectionXML.Element("SourceID").Value);
					Guid sinkID = new Guid(connectionXML.Element("SinkID").Value);

					String sourceConnectorName = connectionXML.Element("SourceConnectorName").Value;
					String sinkConnectorName = connectionXML.Element("SinkConnectorName").Value;

					Connector sourceConnector = GetConnector(sourceID, sourceConnectorName);
					Connector sinkConnector = GetConnector(sinkID, sinkConnectorName);

					Connection connection = new Connection(sourceConnector, sinkConnector);
					Canvas.SetZIndex(connection, Int32.Parse(connectionXML.Element("zIndex").Value));
					this.Children.Add(connection);
				}
			} catch (ArgumentNullException ex) {
				throw new FileFormatException("File is corrupted.", ex);
			} catch (FormatException ex) {
				throw new FileFormatException("File is corrupted.", ex);
			} catch (OverflowException ex) {
				throw new FileFormatException("File is corrupted.", ex);
			}
		}

		/// <summary>
		/// Deserializes DesignerItem.
		/// </summary>
		/// <param name="itemXML">root element with serialized DesignerItem</param>
		/// <param name="id">Guid of this DesignerItem</param>
		/// <param name="OffsetX">x offset</param>
		/// <param name="OffsetY">y offset</param>
		/// <param name="documentParts">reference to list of XElement (documentParts)</param>
		/// <returns>deserialzed DesignerItem</returns>
		/// <exception cref="System.IO.FileFormatException">File is corrupted.</exception>
		private DesignerItem DeserializeDesignerItem(XElement itemXML, Guid id, double OffsetX, double OffsetY, IEnumerable<XElement> documentParts) {
			try {
				double width = Double.Parse(itemXML.Element("Width").Value, CultureInfo.InvariantCulture);
				double height = Double.Parse(itemXML.Element("Height").Value, CultureInfo.InvariantCulture);
				Guid parentID = new Guid(itemXML.Element("ParentID").Value);
				bool isGroup = Boolean.Parse(itemXML.Element("IsGroup").Value);
				double left = Double.Parse(itemXML.Element("Left").Value, CultureInfo.InvariantCulture) + OffsetX;
				double top = Double.Parse(itemXML.Element("Top").Value, CultureInfo.InvariantCulture) + OffsetY;
				int zIndex = Int32.Parse(itemXML.Element("zIndex").Value, CultureInfo.InvariantCulture);

				string contentType = itemXML.Element("ContentType").Value;
				if (String.IsNullOrEmpty(contentType))
					throw new FileFormatException("ContentType is empty.");

				Thickness padding = (Thickness)new ThicknessConverter().ConvertFrom(itemXML.Element("Padding").Value);
				Color color = (Color)ColorConverter.ConvertFromString(itemXML.Element("FillBrush").Value);
				if (color == null)
					throw new FileFormatException("FillColor is corrupted.");
				SolidColorBrush fillBrush = new SolidColorBrush(color);

				Color color2 = (Color)ColorConverter.ConvertFromString(itemXML.Element("StrokeBrush").Value);
				if (color2 == null)
					throw new FileFormatException("StrokeColor is corrupted.");
				SolidColorBrush strokeBrush = new SolidColorBrush(color2);

				// XNodeInfo part
				XNodeInfo info = XNodeInfo.Deserialize(itemXML.Element("XNodeInfo"));

				// Create DesignerItem
				DesignerItem item = new DesignerItem(id, (ControlTemplate)FindResource(info.AppearanceTemplate), info);

				// Set its properties
				item.Width = width;
				item.ParentID = parentID;
				item.IsGroup = isGroup;
				item.Padding = padding;
				item.FillBrush = fillBrush;
				item.StrokeBrush = strokeBrush;

				Canvas.SetLeft(item, left);
				Canvas.SetTop(item, top);
				Canvas.SetZIndex(item, zIndex);

				// get XmlVisualizerNamespace
				string xmlVisualizerNamespace = ConfigurationManager.AppSettings["xmlvisualizerNamespace"];
				XNamespace ns = xmlVisualizerNamespace;
				XName name = ns.GetName("ID");

				bool found = false;
				// find XNode for DesignerItem
				foreach (XElement rootElement in documentParts) {
					// find the right part by ID (Guid)
					var res = (from r in rootElement.DescendantsAndSelf() where (r.Attribute(name).Value == info.ID.ToString()) select r).FirstOrDefault();
					if (res is XElement) {
						found = true;
						XElement el = res as XElement;

						if (contentType == typeof(ucElement).ToString()) {
							ucElement element = new ucElement(el);
							if (element != null) {
								item.Content = element;
								item.Element = el;
								item.Node = el;

								info.Node = el;
								info.UI = item;

								// add info to DisplayedNodes
								if (Document.Current.DisplayedNodes.ContainsKey(el)) {
									throw new ArgumentException("Duplicate in DisplayedNodes.");
								} else {
									Document.Current.DisplayedNodes.Add(el, info);
								}
								// and to model's XNodeInfoCollection
								Document.CurrentController.AddNodeInfo(info);
							}
						} else if (contentType == typeof(ucTextNode).ToString()) {
							found = true;
							XText text = new XText(el.Value);
							el.AddBeforeSelf(text); // swap <text> element for text node
							XNode node = el.PreviousNode;
							el.Remove();

							ucTextNode textNode = new ucTextNode(node);

							item.Content = textNode;
							item.Node = node;
							item.Element = null;

							info.Node = node;
							info.UI = item;

							// add info to DisplayedNodes
							if (Document.Current.DisplayedNodes.ContainsKey(el.FirstNode)) {
								throw new ArgumentException("Duplicate in DisplayedNodes.");
							} else {
								Document.Current.DisplayedNodes.Add(node, info);
							}
							// and to model's XNodeInfoCollection
							Document.CurrentController.AddNodeInfo(info);
						}
						break;
					}
				}
				if (!found)
					throw new FileFormatException("Associated element wasn't found.");

				return item;
			} catch (XmlException ex) {
				Exceptions.ExceptionHandling.HandleException(ex);
			} catch (ArgumentNullException ex) {
				throw new FileFormatException("File is corrupted.", ex);
			} catch (ArgumentException ex) {
				Exceptions.ExceptionHandling.HandleException(ex);
			} catch (NullReferenceException ex) {
				throw new FileFormatException("File is corrupted.", ex);
			} catch (InvalidOperationException ex) {
				Exceptions.ExceptionHandling.HandleException(ex);
			} catch (FormatException ex) {
				throw new FileFormatException("File is corrupted.", ex);
			} catch (OverflowException ex) {
				throw new FileFormatException("File is corrupted.", ex);
			} catch (NotSupportedException ex) {
				throw new FileFormatException("File is corrupted.", ex);
			}
			return null;
		}

		private void CopyCurrentSelection() {
			IEnumerable<DesignerItem> selectedDesignerItems =
				this.selectionService.CurrentSelection.OfType<DesignerItem>();

			List<Connection> selectedConnections =
				this.selectionService.CurrentSelection.OfType<Connection>().ToList();

			foreach (Connection connection in this.Children.OfType<Connection>()) {
				if (!selectedConnections.Contains(connection)) {
					DesignerItem sourceItem = (from item in selectedDesignerItems
											   where item.ID == connection.Source.ParentDesignerItem.ID
											   select item).FirstOrDefault();

					DesignerItem sinkItem = (from item in selectedDesignerItems
											 where item.ID == connection.Sink.ParentDesignerItem.ID
											 select item).FirstOrDefault();

					if (sourceItem != null &&
						sinkItem != null &&
						BelongToSameGroup(sourceItem, sinkItem)) {
						selectedConnections.Add(connection);
					}
				}
			}

			XElement designerItemsXML = SerializeDesignerItems(selectedDesignerItems);
			XElement connectionsXML = SerializeConnections(selectedConnections);

			XElement root = new XElement("Root");
			root.Add(designerItemsXML);
			root.Add(connectionsXML);

			root.Add(new XAttribute("OffsetX", 10));
			root.Add(new XAttribute("OffsetY", 10));

			Clipboard.Clear();
			Clipboard.SetData(DataFormats.Xaml, root);
		}

		private void DeleteCurrentSelection(bool? recursively) {
			if (recursively == null)
				return;

			foreach (Connection connection in selectionService.CurrentSelection.OfType<Connection>()) {
				connection.Delete();
				this.Children.Remove(connection);
			}

			foreach (DesignerItem item in selectionService.CurrentSelection.OfType<DesignerItem>()) {
				List<Connection> connections = new List<Connection>();
				// delete designer items
				DeleteDesignerItem(item, (bool)recursively, connections);
				// and then connections that were chosen for removal
				foreach (Connection con in connections)
					con.Delete();
			}

			selectionService.ClearSelection();
			UpdateZIndex();
		}

		private void DeleteDesignerItem(DesignerItem item, bool recursively, List<Connection> connectionsToDelete) {
			Control cd = null;
			try {
				cd = item.GetConnectorDecorator();
			} catch (Exception ex) {
				Exceptions.ExceptionHandling.HandleException(ex);
			}

			List<Connector> connectors = new List<Connector>();
			GetConnectors(cd, connectors);

			foreach (Connector connector in connectors) {
				foreach (Connection con in connector.Connections) {
					// case when element has only text content; we want to delete that designer item too
					// regardless of what the user said
					if (item.Node is XElement) {
						XElement element = item.Node as XElement;
						if (element.Elements().Count() == 0)
							recursively = true;
					}

					if (recursively == true) {
						Connector sink = con.Sink;
						if (sink != null) {
							DesignerItem parentSink = sink.ParentDesignerItem;

							if (parentSink != null && parentSink != item) {
								DeleteDesignerItem(parentSink, recursively, connectionsToDelete);
							}
						}
					}
					this.Children.Remove(con);
					connectionsToDelete.Add(con);
				}
			}

			// remove XNode if there is one associated
			if (item.Node != null) {
				Document.CurrentController.RemoveNode(item.Node, recursively);
			}

			this.Children.Remove(item);
		}

		private void UpdateZIndex() {
			List<UIElement> ordered = (from UIElement item in this.Children
									   orderby Canvas.GetZIndex(item as UIElement)
									   select item as UIElement).ToList();

			for (int i = 0; i < ordered.Count; i++) {
				Canvas.SetZIndex(ordered[i], i);
			}
		}

		private static Rect GetBoundingRectangle(IEnumerable<DesignerItem> items) {
			double x1 = Double.MaxValue;
			double y1 = Double.MaxValue;
			double x2 = Double.MinValue;
			double y2 = Double.MinValue;

			foreach (DesignerItem item in items) {
				x1 = Math.Min(Canvas.GetLeft(item), x1);
				y1 = Math.Min(Canvas.GetTop(item), y1);

				x2 = Math.Max(Canvas.GetLeft(item) + item.Width, x2);
				y2 = Math.Max(Canvas.GetTop(item) + item.Height, y2);
			}

			return new Rect(new Point(x1, y1), new Point(x2, y2));
		}

		private void GetConnectors(DependencyObject parent, List<Connector> connectors) {
			int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
			for (int i = 0; i < childrenCount; i++) {
				DependencyObject child = VisualTreeHelper.GetChild(parent, i);
				if (child is Connector) {
					connectors.Add(child as Connector);
				} else
					GetConnectors(child, connectors);
			}
		}

		public Connector GetConnector(Guid itemID, String connectorName) {
			DesignerItem designerItem = (from item in this.Children.OfType<DesignerItem>()
										 where item.ID == itemID
										 select item).FirstOrDefault();

			Control connectorDecorator = designerItem.Template.FindName("PART_ConnectorDecorator", designerItem) as Control;
			connectorDecorator.ApplyTemplate();

			return connectorDecorator.Template.FindName(connectorName, connectorDecorator) as Connector;
		}

		private bool BelongToSameGroup(IGroupable item1, IGroupable item2) {
			IGroupable root1 = selectionService.GetGroupRoot(item1);
			IGroupable root2 = selectionService.GetGroupRoot(item2);

			return (root1.ID == root2.ID);
		}

		#endregion
	}
}